package server;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;

/**
 * @author songyi
 * @date 2020/6/28 0028 10:54
 * @description minicat的主类
 */
public class BootStrap {
    /*//定义一个端口号
    private int port = 8080;

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }*/
    private Mapper mapper = new Mapper();

    /**
     * minicat启动需要初始化的操作
     */
    public void start() throws Exception {

        // 加载解析相关的配置：server.xml
        loadServer();
        /*// 加载解析相关的配置：web.xml
        loadServlet();*/

        /**
         * 定义一个线程池
         */
        // 初始大小
        int corePoolSize = 10;
        // 最大
        int maximumPoolSize = 50;
        // 保持活跃时长
        long keepAliveTime = 100L;
        // 单位，这里设置为秒
        TimeUnit unit = TimeUnit.SECONDS;
        // 请求的队列
        BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<Runnable>(50);
        ThreadFactory threadFactory = Executors.defaultThreadFactory();
        // 拒绝策略
        RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);

        // 完成1.0版本（请求：localhost:8080，返回一个固定的字符串到页面）
        ServerSocket serverSocket = new ServerSocket(mapper.getPort());
        System.out.println("Minicat start on port: " + mapper.getPort());
        /*while (true){
            // 有了socket，接收请求
            Socket socket = serverSocket.accept();
            // 获取输出流
            OutputStream outputStream = socket.getOutputStream();
            String data = "Hello Minicat";
            String responseText = HttpProtocoUtil.getHeader200(data.getBytes().length)+ data;
            outputStream.write(responseText.getBytes());
            outputStream.close();
            socket.close();
        }*/

        // 完成2.0版本（封装Request和Response对象，返回html静态资源文件）
        /*while (true) {
            Socket socket = serverSocket.accept();
            InputStream inputStream = socket.getInputStream();
            OutputStream outputStream = socket.getOutputStream();
            // 封装request和response对象
            Request request = new Request(inputStream);
            Response response = new Response(outputStream);
            response.outputHtml(request.getUrl());
            inputStream.close();
            outputStream.close();
            socket.close();
        }*/

        // 完成3.0版本（可以请求动态资源servlet）
        /*while (true) {
            Socket socket = serverSocket.accept();
            InputStream inputStream = socket.getInputStream();
            OutputStream outputStream = socket.getOutputStream();
            // 封装request和response对象
            Request request = new Request(inputStream);
            Response response = new Response(outputStream);
            // 静态资源处理
            if (servletMap.get(request.getUrl()) == null) {
                response.outputHtml(request.getUrl());
            } else {
                // 动态资源servlet处理
                HttpServlet httpServlet = servletMap.get(request.getUrl());
                httpServlet.service(request, response);
            }
            inputStream.close();
            outputStream.close();
            socket.close();
        }*/

        // 完成4.0版本，多线程改造（非线程池方式）
        /*while (true) {
            Socket socket = serverSocket.accept();
            RequestProcessor requestProcessor = new RequestProcessor(socket, servletMap);
            requestProcessor.start();
            // socket.close();
        }*/

        // 完成4.0版本，多线程改造（线程池方式）
        while (true) {
            Socket socket = serverSocket.accept();
            RequestProcessor requestProcessor = new RequestProcessor(socket, mapper);
            threadPoolExecutor.execute(requestProcessor);
        }

    }

    // 加载解析相关的配置：server.xml
    private void loadServer() {
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("server.xml");
        SAXReader saxReader = new SAXReader();
        try {
            Document document = saxReader.read(resourceAsStream);
            Element rootElement = document.getRootElement();
            // 解析Connector标签，获取port属性值
            Element service = (Element) rootElement.selectSingleNode("Service");
            Element connector = (Element) service.selectSingleNode("Connector");
            String port = connector.attributeValue("port");
            // 监听端口
            mapper.setPort(Integer.parseInt(port));
            // 解析Engine标签
            Element engine = (Element) service.selectSingleNode("Engine");
            List<Element> hosts = engine.selectNodes("Host");
            for (Element host : hosts) {
                //解析并加载host标签属性
                loadHost(host);
            }
        } catch (DocumentException | IOException e) {
            e.printStackTrace();
        }
    }

    // 解析并加载host标签属性
    private void loadHost(Element host) throws IOException {
        MappedHost mappedHost = new MappedHost();
        String name = host.attributeValue("name");
        mappedHost.setName(name);
        // webapps绝对路径
        String appBase = host.attributeValue("appBase");
        mappedHost.setAppBase(appBase);
        // 文件夹
        File webapps = new File(host.attributeValue("appBase"));
        File[] apps = webapps.listFiles();
        // 遍历webapps下项目
        for (File app : apps) {
            // 项目名称
            String webAppName = app.getName();
            // 每个项目中web.xml绝对路径
            String webXmlPath = app.getPath() + "\\web.xml";
            // 解析每个项目下的web.xml
            loadServlet(mappedHost, appBase, webAppName, webXmlPath);
            // 初始化完成，存入hostMap中
            mapper.getHostMap().put(name, mappedHost);
        }
    }

    private Map<String, HttpServlet> servletMap = new HashMap<String, HttpServlet>();

    // 加载解析web.xml，初始化servlet
    private void loadServlet(MappedHost mappedHost, String appBase, String webAppName, String webXmlPath) throws IOException {
        MappedContext mappedContext = new MappedContext();
        mappedContext.setName(webAppName);
        mappedHost.getContextMap().put(webAppName, mappedContext);
        try {
            File file = new File(webXmlPath);
            InputStream resourceAsStream = new FileInputStream(file);
            SAXReader saxReader = new SAXReader();
            Document document = saxReader.read(resourceAsStream);
            Element rootElement = document.getRootElement();
            List<Element> selectNodes = rootElement.selectNodes("//servlet");
            for (Element element : selectNodes) {
                // <servlet-name>myServlet</servlet-name>
                Element servletNameElement = (Element) element.selectSingleNode("servlet-name");
                String servletName = servletNameElement.getStringValue();
                // <servlet-class>server.MyServlet</servlet-class>
                Element servletClassElement = (Element) element.selectSingleNode("servlet-class");
                String servletClass = servletClassElement.getStringValue();
                // 根据servlet-name的值找到url-pattern
                Element servletMapping = (Element) rootElement.selectSingleNode("/web-app/servlet-mapping[servlet-name='" + servletName + "']");
                String urlPattern = servletMapping.selectSingleNode("url-pattern").getStringValue();

                // 自定义servlet的路径
                String servletClassPath = appBase + "/" + webAppName + "/";
                FileClassLoader fileClassLoader = new FileClassLoader(servletClassPath);
                Class bytesFromFile = fileClassLoader.getBytesFromFile(servletClass);
                // 反射得到HttpServlet对象，存入ServletMap中
                mappedContext.getServletMap().put(urlPattern, (HttpServlet) bytesFromFile.newInstance());
            }
        } catch (DocumentException e) {
            e.printStackTrace();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }

    }

    /*// 加载解析web.xml，初始化servlet
    private void loadServlet() {
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("web.xml");
        SAXReader saxReader = new SAXReader();
        try {
            Document document = saxReader.read(resourceAsStream);
            Element rootElement = document.getRootElement();
            List<Element> selectNodes = rootElement.selectNodes("//servlet");
            for (Element element : selectNodes) {
                // <servlet-name>myServlet</servlet-name>
                Element servletNameElement = (Element) element.selectSingleNode("servlet-name");
                String servletName = servletNameElement.getStringValue();
                // <servlet-class>server.MyServlet</servlet-class>
                Element servletClassElement = (Element) element.selectSingleNode("servlet-class");
                String servletClass = servletClassElement.getStringValue();
                // 根据servlet-name的值找到url-pattern
                Element servletMapping = (Element) rootElement.selectSingleNode("/web-app/servlet-mapping[servlet-name='" + servletName + "']");
                String urlPattern = servletMapping.selectSingleNode("url-pattern").getStringValue();
                servletMap.put(urlPattern, (HttpServlet) Class.forName(servletClass).newInstance());
            }
        } catch (DocumentException | ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
    }*/

    /**
     * minicat的启动入口
     *
     * @param args
     */
    public static void main(String[] args) {
        BootStrap bootStrap = new BootStrap();
        try {
            bootStrap.start();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
